﻿/*
 * BString
 * 
 * Author: snowdreamist
 * Date: 2011/11/19
 * 
 */

using System;
using System.IO;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using BitTorrentSharp.Helpers;

namespace BitTorrentSharp.BEncoding
{
    public class BString : BValue, IComparable<BString>
    {
        public BString() { }

        public BString(byte[] content)
        {
            this.Content = content;
        }

        public BString(string content)
        {
            if (content == null)
                this.Content = null;
            else
                this.Content = Encoding.Default.GetBytes(content);
        }

        public BString(string content, Encoding encoding)
        {
            if (content == null)
            {
                this.Content = null;
            }
            else if (encoding == null)
            {
                this.Content = Encoding.Default.GetBytes(content);
            }
            else
            {
                this.Content = encoding.GetBytes(content);
                this._knownEncoding = encoding;
            }
        }

        public static implicit operator BString(string source)
        {
            return new BString(source);
        }

        public static implicit operator string(BString source)
        {
            if (source == null)
                return null;
            else
                return source.ToString();
        }

        public static implicit operator byte[](BString source)
        {
            if (source == null)
                return null;
            else
                return source.Content;
        }

        #region private
        /// <summary>
        /// known encoding
        /// </summary>
        private Encoding _knownEncoding;
        #endregion

        /// <summary>
        /// content of this string
        /// </summary>
        public byte[] Content { get; set; }

        #region overrides

        internal protected override void InternalDeserialize(BinaryReader reader)
        {
            var topChar = (char)reader.PeekChar();

            if (topChar >= '0' && topChar < '9')
            {
                // a string
                var lengthStr = reader.ReadStringUntil((byte)':');
                int length = 0;
                if (!int.TryParse(lengthStr, out length))
                    throw new BEncodingException(string.Format("Cannot convert {0} to number", lengthStr));
                if (length <= 0)
                    throw new BEncodingException(string.Format("Invalid string length {0}", length));
                // read string
                this.Content = reader.ReadBytes(length);
            }
            else
            {
                throw new BEncodingException(string.Format("Current element is not a string. Input char:", topChar));
            }
        }

        internal protected override void InternalSerialize(BinaryWriter writer)
        {
            if (this.Content == null)
                throw new BEncodingException("Content cannot be null");
            if (this.Content.Length == 0)
                throw new BEncodingException("Content length cannot be zero");
            // serialize
            string lengthStr = this.Content.Length.ToString();
            // write length
            writer.Write(BValue.GetOrginalBytes(lengthStr));
            // write :
            writer.Write((byte)':');
            // write content
            writer.Write(this.Content);
        }
        /// <summary>
        /// Serialize content equals to serialize
        /// </summary>
        /// <param name="writer"></param>
        protected internal override void InternalSerializeContent(BinaryWriter writer)
        {
            this.InternalSerialize(writer);
        }

        public override int GetHashCode()
        {
            return this.Content != null ? this.Content.GetBytesHashCode() : 0;
        }

        public override bool Equals(object obj)
        {
            BString bString = obj as BString;

            if (bString == null)
                return false;

            return BinaryHelper.CompareBytes(this.Content, bString.Content) == 0;
        }

        public override string ToString()
        {
            return this.ToString(null);
        }

        #endregion

        public string ToString(Encoding encoding)
        {
            if (this.Content == null)
                return null;
            else if (encoding != null)
                return encoding.GetString(this.Content);
            else if (this._knownEncoding != null)
                return this._knownEncoding.GetString(this.Content);
            else
                return Encoding.Default.GetString(this.Content);
        }

        public int CompareTo(BString other)
        {
            return BinaryHelper.CompareBytes(this.Content, other.Content);
        }
    }
}
